Skip to contentMethod: visitIdentifier(String, Object)
1: /**
2: * Copyright (C) 2017 Czech Technical University in Prague
3: * <p>
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: * <p>
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.jsonld.serialization.traversal;
16:
17: import cz.cvut.kbss.jsonld.common.BeanAnnotationProcessor;
18: import cz.cvut.kbss.jsonld.common.BeanClassProcessor;
19: import cz.cvut.kbss.jsonld.common.IdentifierUtil;
20: import cz.cvut.kbss.jsonld.exception.JsonLdSerializationException;
21: import cz.cvut.kbss.jsonld.exception.MissingIdentifierException;
22:
23: import java.lang.reflect.Field;
24: import java.util.*;
25:
26: public class ObjectGraphTraverser implements InstanceVisitor {
27:
28: private final Set<InstanceVisitor> visitors = new HashSet<>(4);
29:
30: private final InstanceTypeResolver typeResolver = new InstanceTypeResolver();
31:
32: private boolean requireId = false;
33:
34: private Map<Object, String> knownInstances;
35:
36: public void addVisitor(InstanceVisitor visitor) {
37: Objects.requireNonNull(visitor);
38: visitors.add(visitor);
39: }
40:
41: public void removeVisitor(InstanceVisitor visitor) {
42: visitors.remove(visitor);
43: }
44:
45: private void resetKnownInstances() {
46: this.knownInstances = new IdentityHashMap<>();
47: }
48:
49: public void traverse(Object instance) {
50: Objects.requireNonNull(instance);
51: resetKnownInstances();
52: try {
53: if (instance instanceof Collection) {
54: traverseCollection((Collection<?>) instance);
55: } else {
56: traverseImpl(instance);
57: }
58: } catch (IllegalAccessException e) {
59: throw new JsonLdSerializationException("Unable to extract field value.", e);
60: }
61: }
62:
63: private void traverseCollection(Collection<?> col) throws IllegalAccessException {
64: openCollection(col);
65: for (Object item : col) {
66: traverseImpl(item);
67: }
68: closeCollection(col);
69: }
70:
71: private void traverseImpl(Object instance) throws IllegalAccessException {
72: if (knownInstances.containsKey(instance)) {
73: visitKnownInstance(knownInstances.get(instance), instance);
74: return;
75: }
76: openInstance(instance);
77: visitIdentifier(null, instance);
78: if (!BeanClassProcessor.isIdentifierType(instance.getClass())) {
79: visitTypes(null, instance);
80: serializeFields(instance);
81: }
82: closeInstance(instance);
83: }
84:
85: private void serializeFields(Object instance) throws IllegalAccessException {
86: final List<Field> fieldsToSerialize =
87: orderAttributesForSerialization(BeanAnnotationProcessor.getSerializableFields(instance),
88: BeanAnnotationProcessor.getAttributeOrder(instance.getClass()));
89: for (Field f : fieldsToSerialize) {
90: if (BeanAnnotationProcessor.isInstanceIdentifier(f)) {
91: continue;
92: }
93: Object value = BeanClassProcessor.getFieldValue(f, instance);
94: visitField(f, value);
95: if (value != null && BeanAnnotationProcessor.isObjectProperty(f)) {
96: traverseObjectPropertyValue(value);
97: }
98: }
99: }
100:
101: private List<Field> orderAttributesForSerialization(List<Field> fields, String[] ordering) {
102: final List<Field> result = new ArrayList<>(fields.size());
103: for (String item : ordering) {
104: final Iterator<Field> it = fields.iterator();
105: while (it.hasNext()) {
106: final Field f = it.next();
107: if (f.getName().equals(item)) {
108: it.remove();
109: result.add(f);
110: break;
111: }
112: }
113: }
114: result.addAll(fields);
115: return result;
116: }
117:
118: private void traverseObjectPropertyValue(Object value) throws IllegalAccessException {
119: if (value instanceof Collection) {
120: final Collection<?> col = (Collection<?>) value;
121: openCollection(col);
122: for (Object elem : col) {
123: traverseImpl(elem);
124: }
125: closeCollection(col);
126: } else if (value.getClass().isArray()) {
127: throw new JsonLdSerializationException("Arrays are not supported, yet.");
128: } else {
129: traverseImpl(value);
130: }
131: }
132:
133: @Override
134: public void openInstance(Object instance) {
135: if (!BeanClassProcessor.isIdentifierType(instance.getClass())) {
136: final Optional<Object> identifier = BeanAnnotationProcessor.getInstanceIdentifier(instance);
137: knownInstances.put(instance, identifier.orElse(IdentifierUtil.generateBlankNodeId()).toString());
138: }
139: visitors.forEach(v -> v.openInstance(instance));
140: }
141:
142: @Override
143: public void closeInstance(Object instance) {
144: visitors.forEach(v -> v.closeInstance(instance));
145: }
146:
147: @Override
148: public void visitKnownInstance(String id, Object instance) {
149: visitors.forEach(v -> v.visitKnownInstance(id, instance));
150: }
151:
152: @Override
153: public void visitIdentifier(String identifier, Object instance) {
154: final String id;
155:• if (BeanClassProcessor.isIdentifierType(instance.getClass())) {
156: id = instance.toString();
157: } else {
158: final Optional<Object> extractedId = BeanAnnotationProcessor.getInstanceIdentifier(instance);
159:• if (!extractedId.isPresent() && requireId) {
160: throw MissingIdentifierException.create(instance);
161: }
162: id = extractedId.orElse(IdentifierUtil.generateBlankNodeId()).toString();
163: knownInstances.put(instance, id);
164: }
165: visitors.forEach(v -> v.visitIdentifier(id, instance));
166: }
167:
168: @Override
169: public void visitTypes(Collection<String> types, Object instance) {
170: final Set<String> resolvedTypes = typeResolver.resolveTypes(instance);
171: assert !resolvedTypes.isEmpty();
172: visitors.forEach(v -> v.visitTypes(resolvedTypes, instance));
173: }
174:
175: @Override
176: public void visitField(Field field, Object value) {
177: visitors.forEach(v -> v.visitField(field, value));
178: }
179:
180: @Override
181: public void openCollection(Collection<?> collection) {
182: visitors.forEach(v -> v.openCollection(collection));
183: }
184:
185: @Override
186: public void closeCollection(Collection<?> collection) {
187: visitors.forEach(v -> v.closeCollection(collection));
188: }
189:
190: public void setRequireId(boolean requireId) {
191: this.requireId = requireId;
192: }
193: }